/*******************************************************************************
 * Copyright 2011 Towee.net
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *******************************************************************************/
package net.towee.client.widgets.tree;

import java.util.ArrayList;
import java.util.List;

import net.towee.client.widgets.ExtFlowPanel;
import net.towee.client.widgets.ListItemWidget;
import net.towee.client.widgets.SpanPanel;
import net.towee.client.widgets.UnorderedListWidget;

import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.HasOpenHandlers;
import com.google.gwt.event.logical.shared.HasSelectionHandlers;
import com.google.gwt.event.logical.shared.OpenEvent;
import com.google.gwt.event.logical.shared.OpenHandler;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.event.logical.shared.SelectionHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Widget;

/**
 * This widget is a tree node.
 * @author viiv.c58@gmail.com (Artem Artem'ev)
 */
public class TreeItem<T> extends ListItemWidget implements HasOpenHandlers<T>, HasSelectionHandlers<T> {
	
	protected Label expand = new Label();
	protected ExtFlowPanel content = new ExtFlowPanel();
	protected UnorderedListWidget container = new UnorderedListWidget(); 
	protected List<TreeItem> children = new ArrayList<TreeItem>();
	protected Tree root = null;
	
	protected boolean selected = false;
	protected boolean opened = false;
	protected boolean leaf = true;
	
	public TreeItem(){
		this(null);
	}
	
	public TreeItem(String text){
		// Set styles
		setStyleName("tn-node");
		addStyleName("tn-leaf");
		container.setStyleName("tn-container");
		expand.setStyleName("tn-expand");
		content.setStyleName("tn-content");
		
		// Make ui
		add(expand);
		add(content);
		add(container);
		
		if(text != null)
			setContent(text);
		
		// Bind events
		bindEvents();
	}
	
	public void setContent(Widget w) {
		content.clear();
		content.add(w);
	}
	
	public void setContent(String t) {
		content.clear();
		content.add(new SpanPanel(t));
	}
	
	private void bindEvents() {
		/**
		 * Select node by click
		 */
		content.addClickHandler(new ClickHandler(){
			@Override
			public void onClick(ClickEvent event) {
				SelectionEvent.fire(TreeItem.this, (T)getLayoutData());
				
				if(selected == false) {
					setSelectState(true, event.isControlKeyDown());
				}
			}
		});
		
		/**
		 * Open node
		 */
		expand.addClickHandler(new ClickHandler(){

			@Override
			public void onClick(ClickEvent event) {
				if(!leaf) {
					setSelectState(true, false);

					if(!isOpen()) {
						OpenEvent.fire(TreeItem.this, (T)getLayoutData());
						setOpenState(true, false);
					}
					else
						setOpenState(false, false);
				}
			}
		});
	}
	
	/**
	 * Return tree root
	 * @return
	 */
	public Tree getRoot() {
		return root;
	}
	
	public void setRoot(Tree r){
		this.root = r;
	}

	/**
	 * Return true if node opened, and false otherwise
	 * @return
	 */
	public boolean isOpen() {
		return opened;
	}
	
	/**
	 * Set opened state
	 * @param state true for opened node, false for close
	 * @param recurciv open this node(open/close all parent nodes)
	 */
	public void setOpenState(boolean state, boolean r) {
		this.opened = state;
		
		if(opened == true) {
			removeStyleName("tn-closed");
			addStyleName("tn-opened");
			getRoot().nodeOpened(this);
		}	else {
			removeStyleName("tn-opened");
			addStyleName("tn-closed");
			getRoot().nodeClosed(this);
		}
		
		// Open parent
		if(r == true && getParentNode() != null)
			getParentNode().setOpenState(state, true);
	}
	
	/**
	 * Select node and fire event
	 * @param s
	 * @param r
	 */
	public void setSelectState(boolean s, boolean r) {
		setSelectState(s, r, true);
	}
	
	/**
	 * Set selected sate
	 * @param s selected state. True for selecting and false for unslecting
	 * @param r if false -> unselect all selected nodes
	 * @param a fire event
	 */
	public void setSelectState(boolean s, boolean r, boolean f) {
		this.selected = s;
		
		if(selected == true) {
			// Unselect if needed
			if(r == false)
				root.unselectNodes();
			
			// Add node as selected
			root.addSelectedNode(this, r, f);
			
			// Set style
			content.addStyleName("tn-selected");
		}	else {
			content.removeStyleName("tn-selected");
		}
	}

	/**
	 * This method return parent TreeNode. It this doesn't have
	 * parent node, it return null
	 * @return
	 */
	public TreeItem getParentNode() {
		if(getParent().getParent() instanceof TreeItem)
			return (TreeItem)getParent().getParent();
		return null;
	}

	/**
	 * Add child node to this node.
	 * @param child
	 * @return index of added child
	 */
	public int addChildNode(TreeItem child) {
		if(leaf) setLeaf(false);
		
		child.setRoot(root);
		container.add(child);
		children.add(child);
		return container.getWidgetIndex(child);
	}
	
	public void setLeaf(boolean l) {
		leaf = l;
		
		if(leaf) {
			addStyleName("tn-leaf");
			removeStyleName("tn-opened");
			removeStyleName("tn-closed");
		}	else {
			removeStyleName("tn-leaf");
			setOpenState(opened, false);
		}		
	}
	
	/**
	 * Close all children
	 * @param r true if we need to close all subchild
	 */
	public void closeAll(boolean r){
		for(TreeItem node : children){
			node.setOpenState(false, false);
			
			if(r == true)
				node.closeAll(r);
		}
	}

	@Override
	public HandlerRegistration addOpenHandler(OpenHandler<T> handler) {
		return addHandler(handler, OpenEvent.getType());
	}
	
	@Override
	public HandlerRegistration addSelectionHandler(SelectionHandler<T> handler) {
		return addHandler(handler, SelectionEvent.getType());
	}

	/**
	 * Remove all children
	 */
	public void removeChildren() {
		for(TreeItem node : children){
			node.removeFromParent();
		}		
		
		children.clear();
	}
}
